package com.tramp.utils;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.google.common.collect.Lists;
import com.tramp.frame.server.exception.GenericException;

public final class ExcelWebUtil {

	private static final Logger LOGGER = LoggerFactory.getLogger(ExcelWebUtil.class);

	private static final String FRAME_PARAM_EXPORT_TYPE = "frameParamExportType"; //导出类型
	private static final String FRAME_PARAM_EXPORT_TYPE_EXCEL = "excel"; //导出类型-excel
	private static final String FRAME_PARAM_EXPORT_FIELD_KEY = "frameParamExportFieldKeys"; //要导出的字段，与Entity的属性对应
	private static final String FRAME_PARAM_EXPORT_FIELD_NAME = "frameParamExportFieldNames"; //要导出字段的EXCEL表头名称
	private static final String FRAME_PARAM_EXPORT_FILE_NAME_PREFIX = "frameParamExportFileNamePrefix"; //导出的文件名前缀
	private static final String FRAME_PARAM_EXPORT_FILE_NAME_PREFIX_DEFAULT = "CI_EXCEL"; //导出的文件名前缀默认值

	private ExcelWebUtil() {
	}

	public static Boolean isExportExcel(HttpServletRequest request) {
		if (null == request) {
			return false;
		}
		return FRAME_PARAM_EXPORT_TYPE_EXCEL.equals(request.getParameter(FRAME_PARAM_EXPORT_TYPE));
	}

	public static void tryExportExcel(HttpServletResponse response, List<Map<String, Object>> data, String exportFileNamePrefix, String exportKeyList,
			String exportNameList) {
		doExport(response, data, exportKeyList.split(","), exportNameList.split(","), exportFileNamePrefix);
	}

	public static void tryExportExcel(Object data) {
		RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
		if (null == requestAttributes) {
			return;
		}
		HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
		if (!isExportExcel(request)) {
			return;
		}
		if (null == data) {
			throw new GenericException("没有需要导出的数据！");
		}
		String exportFieldKeys = request.getParameter(FRAME_PARAM_EXPORT_FIELD_KEY);
		String exportFieldNames = request.getParameter(FRAME_PARAM_EXPORT_FIELD_NAME);
		if (StringUtils.isAnyBlank(exportFieldKeys, exportFieldNames)) {
			throw new GenericException("导出失败，exportFieldKeys或exportFieldNames参数不正确");
		}
		String exportFileNamePrefix = request.getParameter(FRAME_PARAM_EXPORT_FILE_NAME_PREFIX);
		if (StringUtils.isBlank(exportFileNamePrefix)) {
			exportFileNamePrefix = FRAME_PARAM_EXPORT_FILE_NAME_PREFIX_DEFAULT;
		}
		HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
		if (data instanceof List) {
			String[] fieldKeys = JSONUtils.fromJson(exportFieldKeys, String[].class);
			String[] fieldNames = JSONUtils.fromJson(exportFieldNames, String[].class);
			doExport(response, (List) data, fieldKeys, fieldNames, exportFileNamePrefix);
		}
		else {
			LOGGER.warn("export fail,controller return is not List!url:" + request.getRequestURI());
			throw new GenericException("导出失败，返回值不是List类型");
		}
	}

	private static <X> void doExport(HttpServletResponse response, List<X> listData, String[] fieldKeys, String[] fieldNames, String fileNamePrefix) {
		if (null == response) {
			LOGGER.error("Export Excel fail,response is null!");
			throw new GenericException("导出失败，系统异常！");
		}
		if (CollectionUtils.isEmpty(listData)) {
			throw new GenericException("没有需要导出的数据！");
		}
		if (null == fieldKeys || fieldKeys.length <= 0 || null == fieldNames) {
			throw new GenericException("导出失败，fieldKey与fieldName参数不正确！");
		}
		if (fieldKeys.length != fieldNames.length) {
			throw new GenericException("导出失败，fieldKey与fieldName数量不相等！");
		}

		boolean isMap = false;
		X data = listData.get(0);
		if (null == data) {
			LOGGER.error("Export Excel fail,listData.get(0) is null");
			throw new GenericException("导出失败，系统异常！");
		}
		List exportMethodList = Lists.newArrayList();
		List<String> exportNameList = Lists.newArrayList();
		List argList = Lists.newArrayList();
		if (data instanceof Map) {
			isMap = true;
		}
		Method[] methods = data.getClass().getMethods();
		for (int i = 0; i < fieldKeys.length; i++) {
			exportNameList.add(fieldNames[i]);
			if (isMap) {
				try {
					exportMethodList.add(Map.class.getMethod("get", Object.class));
				}
				catch (NoSuchMethodException e) {
					throw new GenericException("导出失败，系统异常！");
				}
				argList.add(fieldKeys[i]);
				continue;
			}
			String fieldKeyMethod = "GET" + StringUtils.upperCase(fieldKeys[i]);
			for (Method method : methods) {
				if (StringUtils.equals(fieldKeyMethod, StringUtils.upperCase(method.getName()))) {
					exportMethodList.add(method);
					break;
				}
			}
		}
		if (CollectionUtils.isEmpty(exportMethodList)) {
			throw new GenericException("导出失败，没有找到要导出的字段");
		}

		// 声明一个工作薄
		HSSFWorkbook workbook = new HSSFWorkbook();

		//设置超链接样式
		HSSFCellStyle linkStyle = workbook.createCellStyle();
		HSSFFont cellFont = workbook.createFont();
		cellFont.setUnderline((byte) 1);
		cellFont.setColor(HSSFColor.BLUE.index);
		linkStyle.setFont(cellFont);

		// 生成一个表格
		HSSFSheet sheet = workbook.createSheet();
		write2Sheet(sheet, exportMethodList, exportNameList, argList, listData, linkStyle);
		OutputStream stream = null;
		Boolean isExportSuccess = true;
		try {
			response.setCharacterEncoding("utf-8");
			response.setContentType("multipart/form-data");
			String fileName = new String(
					(fileNamePrefix + DateUtil.longToString(System.currentTimeMillis(), DateUtil.YYYYMMDDHH24MMSS) + ".xls").getBytes("utf-8"),
					"ISO-8859-1");
			response.setHeader("Content-Disposition", "attachment;fileName=" + fileName);
			stream = response.getOutputStream();
			workbook.write(stream);
		}
		catch (Exception e) {
			response.reset();
			isExportSuccess = false;
			LOGGER.error(e.toString(), e);
			throw new GenericException("导出EXCEL时发生错误");
		}
		finally {
			if (isExportSuccess && null != stream) {
				try {
					stream.close();
				}
				catch (IOException e) {
					LOGGER.error("close export excel stream error!", e);
				}
			}
		}
	}

	private static <T> void write2Sheet(HSSFSheet sheet, List exportMethodList, List<String> exportNameList, List argList, Collection<T> dataset,
			HSSFCellStyle linkStyle) {
		SimpleDateFormat sdf = new SimpleDateFormat(DateUtil.YYYY_MM_DD_HH_24MM_SS);

		// 产生表格标题行
		HSSFRow row = sheet.createRow(0);

		for (int i = 0; i < exportNameList.size(); i++) {
			HSSFCell cell = row.createCell(i);
			HSSFRichTextString text = new HSSFRichTextString(exportNameList.get(i));
			cell.setCellValue(text);
		}

		// 遍历集合数据，产生数据行
		generateData(sheet, exportMethodList, argList, dataset, sdf, linkStyle);

		// 设定宽度
		for (int i = 0; i < exportNameList.size(); i++) {
			sheet.autoSizeColumn(i);
			int width = sheet.getColumnWidth(i);
			if (width > 9000) {
				sheet.setColumnWidth(i, 9000);
			}
			if (width <= 2000) {
				sheet.setColumnWidth(i, 2000);
			}
		}
	}

	private static <T> void generateData(HSSFSheet sheet, List<Method> exportMethodList, List argList, Collection<T> dataset, SimpleDateFormat sdf,
			HSSFCellStyle linkStyle) {
		HSSFRow row;
		Iterator<T> it = dataset.iterator();
		int index = 0;
		while (it.hasNext()) {
			index++;
			row = sheet.createRow(index);
			T data = it.next();
			try {
				int cellNum = 0;
				for (int i = 0; i < exportMethodList.size(); i++) {
					HSSFCell cell = row.createCell(cellNum);
					Object value;
					if (CollectionUtils.isNotEmpty(argList)) {
						value = exportMethodList.get(i).invoke(data, argList.get(i));
					}
					else {
						value = exportMethodList.get(i).invoke(data);
					}
					setCellValue(value, cell, sdf, linkStyle);
					cellNum++;
				}

			}
			catch (Exception e) {
				LOGGER.error(e.toString(), e);
			}
		}
	}

	private static void setCellValue(Object value, HSSFCell cell, SimpleDateFormat sdf, HSSFCellStyle linkStyle) {
		String textValue = null;
		if (value instanceof Integer) {
			int intValue = (Integer) value;
			cell.setCellValue(intValue);
		}
		else if (value instanceof Float) {
			float floatValue = (Float) value;
			cell.setCellValue(floatValue);
		}
		else if (value instanceof Double) {
			double doubleValue = (Double) value;
			cell.setCellValue(doubleValue);
		}
		else if (value instanceof Long) {
			long longValue = (Long) value;
			cell.setCellValue(longValue);
		}
		else if (value instanceof Boolean) {
			boolean booleanValue = (Boolean) value;
			cell.setCellValue(booleanValue);
		}
		else if (value instanceof Date) {
			Date date = (Date) value;
			textValue = sdf.format(date);
		}
		else {
			// 其它数据类型都当作字符串简单处理
			String empty = org.apache.commons.lang3.StringUtils.EMPTY;
			textValue = value == null ? empty : value.toString();
		}
		if (textValue != null) {
			Pattern pattern = Pattern.compile("^<a href = \".*\">.*</a>$");
			Matcher matcher = pattern.matcher(textValue);
			if (matcher.find()) { //若有a链接，则设置为超链接
				String address = textValue.split("<a href = \"")[1].split("\">.*</a>")[0];
				textValue = textValue.split("<a href = \".*\">")[1].split("</a>")[0];
				HSSFHyperlink link = new HSSFHyperlink(HSSFHyperlink.LINK_URL);
				link.setAddress(address);
				cell.setHyperlink(link);
				cell.setCellStyle(linkStyle);
			}
			HSSFRichTextString richString = new HSSFRichTextString(textValue);
			cell.setCellValue(richString);
		}
	}
}
